#include "Value.h"

#pragma warning(disable: 4244)

Value::Value()
{
	Data = new int;
	*(int *)Data = 0;
	memset(name, 0, NAMELEN);
	valueType = AT_UNKNOWN;
}

//Value::Value(Value &Source)
//{
//	Data = new int;
//	*(int *)Data = 0;
//	strcpy_s(name, NAMELEN, Source.name);
//	valueType = AT_INT;
//	Copy(Source);
//}

Value::Value(const Value &Source)
{
	Data = new int;
	*(int *)Data = 0;
	strcpy_s(name, NAMELEN, Source.name);
	valueType = AT_INT;
	Copy(Source);
}

Value::Value(int Source)
{
	memset(name, 0, NAMELEN);
	valueType = AT_INT;
	Data = new int;
	*(int *)Data = Source;
}

Value::Value(float Source)
{
	memset(name, 0, NAMELEN);
	valueType = AT_FLOAT;
	Data = new float;
	*(float *)Data = Source;
}

Value::Value(const char* Source)
{
	memset(name, 0, NAMELEN);
	valueType = AT_STRING;
	Data = new char[strlen(Source) + sizeof(int) + 1];
	*(int *)Data = strlen(Source);
	Data = ((char*)Data)+sizeof(int);
	strcpy_s((char*)Data, strlen(Source)+1, Source);
}

Value::Value(const char *Name, int Source)
{
	strcpy_s(name, NAMELEN, Name);
	valueType = AT_INT;
	Data = new int;
	*(int*)Data = Source;
}

Value::Value(const char *Name, float Source)
{
	strcpy_s(name, NAMELEN, Name);
	valueType = AT_INT;
	Data = new int;
	*(int*)Data = Source;
}

Value::Value(const char *Name, const char *Source)
{
	strcpy_s(name, NAMELEN, Name);
	valueType = AT_STRING;
	Data = new char[strlen(Source) + sizeof(int) + 1];
	*(int *)Data = strlen(Source);
	Data = ((char*)Data)+sizeof(int);
	strcpy_s((char*)Data, strlen(Source)+1, Source);
}

Value::~Value()
{
	if(GetType() == AT_STRING)
		Data = ((char*)Data) - 4;
	delete Data;
}

ValueType Value::GetType() const
{
	return valueType;
}

void Value::SetType(ValueType newType)
{
	//Comeback and change this to support changing to string from int or float
	if(valueType == newType)
		return;
	Value Old = *this;
	*this = 0;
	valueType = newType;
	if(newType == AT_STRING)
		if(Old.GetType() != AT_STRING)
		{
			delete Data;
			Data = new char[sizeof(int)+1];
			*(int *)Data = 0;
			Data = ((char*)Data)+sizeof(int);
			((char*)Data)[0] = 0;
		}
	*this = Old;
}

Value & Value::operator = (const Value &Source)
{
	Copy(Source);
	return *this;
}

Value Value::operator + (const Value &Source) const
{
	Value Return = *this;

	if(GetType() == AT_UNKNOWN || Source.GetType() == AT_UNKNOWN)
		return Return;
	else if(GetType() == AT_INT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(int*)Return.Data = *(int*)Data + *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(int*)Return.Data = *(int*)Data + *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_FLOAT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(float*)Return.Data = *(float*)Data + *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(float*)Return.Data = *(float*)Data + *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_STRING)
	{
		char Buffer[256];
		char *Buffer2;
		if(Source.GetType() == AT_INT)
		{
			_itoa_s(*(int*)Source.Data,Buffer, 256, 10);
			Buffer2 = new char[*(int *)(((char*)Data)-4) + strlen(Buffer) + 1];
			strcpy_s(Buffer2, *(int *)(((char*)Data)-4) + strlen(Buffer)+1, (char*)Data);
			strcat_s(Buffer2, *(int *)(((char*)Data)-4) + strlen(Buffer)+1, Buffer);
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			sprintf_s(Buffer, 256, "%f", *(float*)Source.Data);
			Buffer2 = new char[*(int *)(((char*)Data)-4) + strlen(Buffer) + 1];
			strcpy_s(Buffer2, *(int *)(((char*)Data)-4) + strlen(Buffer)+1, (char*)Data);
			strcat_s(Buffer2, *(int *)(((char*)Data)-4) + strlen(Buffer)+1, Buffer);
		}
		else if(Source.GetType() == AT_STRING)
		{
			Buffer2 = new char[*(int *)(((char*)Data)-4) + *(int *)(((char*)Source.Data)-4) + 1];
			sprintf_s(Buffer2, *(int *)(((char*)Data)-4) + *(int *)(((char*)Source.Data)-4)+1, "%s%s", (char*)Data, (char*)Source.Data);
		}

		Return.Data = new char[strlen(Buffer2) + sizeof(int) +1];
		*(int*)Return.Data = strlen(Buffer2);
		Return.Data = (void*)(((char*)Return.Data)+sizeof(int));
		strcpy_s((char*)Return.Data, *(int*)(((char *)Return.Data)-sizeof(int))+1, Buffer2);
		delete[] Buffer2;
	}

	return Return;
}

Value Value::operator - (const Value &Source) const
{
	Value Return = *this;

	if(GetType() == AT_UNKNOWN || Source.GetType() == AT_UNKNOWN)
		return Return;
	else if(GetType() == AT_INT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(int*)Return.Data = *(int*)Data - *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(int*)Return.Data = *(int*)Data - *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_FLOAT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(float*)Return.Data = *(float*)Data - *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(float*)Return.Data = *(float*)Data - *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_STRING)
	{
	}

	return Return;
}

Value Value::operator * (const Value &Source) const
{
	Value Return = *this;

	if(GetType() == AT_UNKNOWN || Source.GetType() == AT_UNKNOWN)
		return Return;
	else if(GetType() == AT_INT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(int*)Return.Data = *(int*)Data * *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(int*)Return.Data = *(int*)Data * *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_FLOAT)
	{
		if(Source.GetType() == AT_INT)
		{
			*(float*)Return.Data = *(float*)Data * *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT)
		{
			*(float*)Return.Data = *(float*)Data * *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_STRING)
	{
	}

	return Return;
}

Value Value::operator / (const Value &Source) const
{
	Value Return = *this;

	if(GetType() == AT_UNKNOWN || Source.GetType() == AT_UNKNOWN)
		return Return;
	else if(GetType() == AT_INT)
	{
		if(Source.GetType() == AT_INT && *(int*)Source.Data != 0)
		{
			*(int*)Return.Data = *(int*)Data / *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT && *(float*)Source.Data != 0)
		{
			*(int*)Return.Data = *(int*)Data / *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_FLOAT)
	{
		if(Source.GetType() == AT_INT && *(int*)Source.Data != 0)
		{
			*(float*)Return.Data = *(float*)Data / *(int*)Source.Data;
		}
		else if(Source.GetType() == AT_FLOAT && *(float*)Source.Data != 0)
		{
			*(float*)Return.Data = *(float*)Data / *(float*)Source.Data;
		}
	}
	else if(GetType() == AT_STRING)
	{
	}

	return Return;
}

Value & Value::operator += (const Value &Source)
{
	return *this = *this + Source;
}

Value & Value::operator -= (const Value &Source)
{
	return *this = *this - Source;
}

Value & Value::operator *= (const Value &Source)
{
	return *this = *this * Source;
}

Value & Value::operator /= (const Value &Source)
{
	return *this = *this / Source;
}

bool Value::operator == (const Value &Source) const
{
	bool Return;

	switch(GetType())
	{
	case AT_INT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(int*)Data == *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(int*)Data == *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_FLOAT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(float*)Data == *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(float*)Data == *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_STRING:
		switch(Source.GetType())
		{
		case AT_STRING:
			Return = strcmp((char*)Data, Source.ToCharPtr()) == 0;
			break;
		case AT_INT:
		case AT_FLOAT:
		case AT_UNKNOWN:
		default:
			return false;
		};
	};

	return Return;
}

bool Value::operator != (const Value &Source) const
{
	return !(*this == Source);
}

bool Value::operator < (const Value &Source) const
{
	bool Return;

	switch(GetType())
	{
	case AT_INT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(int*)Data < *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(int*)Data < *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_FLOAT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(float*)Data < *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(float*)Data < *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_STRING:
		switch(Source.GetType())
		{
		case AT_STRING:
			Return = strcmp((char*)Data, Source.ToCharPtr()) < 0;
			break;
		case AT_INT:
		case AT_FLOAT:
		case AT_UNKNOWN:
		default:
			return false;
		};
	};

	return Return;
}

bool Value::operator > (const Value &Source) const
{
	bool Return;

	switch(GetType())
	{
	case AT_INT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(int*)Data > *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(int*)Data > *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_FLOAT:
		switch(Source.GetType())
		{
		case AT_INT:
			Return = *(float*)Data > *(int*)Source.Data;
			break;
		case AT_FLOAT:
			Return = *(float*)Data > *(float*)Source.Data;
			break;
		case AT_STRING:
		case AT_UNKNOWN:
		default:
			return false;
		};
		break;
	case AT_STRING:
		switch(Source.GetType())
		{
		case AT_STRING:
			Return = strcmp((char*)Data, Source.ToCharPtr()) > 0;
			break;
		case AT_INT:
		case AT_FLOAT:
		case AT_UNKNOWN:
		default:
			return false;
		};
	};

	return Return;
}

bool Value::operator <= (const Value &Source) const
{
	return (*this < Source || *this == Source);
}

bool Value::operator >= (const Value &Source) const
{
	return (*this > Source || *this == Source);
}

int Value::ToInt() const
{
	int Return;
	switch(GetType())
	{
	case AT_INT:
		Return = *(int*)Data;
		break;
	case AT_FLOAT:
		Return = (int)*(float*)Data;
		break;
	case AT_STRING:
	case AT_UNKNOWN:
	default:
		Return = -1;
	};
	return Return;
}

float Value::ToFloat() const
{
	float Return;
	switch(GetType())
	{
	case AT_INT:
		Return = (float)*(int*)Data;
		break;
	case AT_FLOAT:
		Return = *(float*)Data;
		break;
	case AT_STRING:
	case AT_UNKNOWN:
	default:
		Return = -1;
	};
	return Return;
}

const char *Value::ToCharPtr() const
{
	char *Return;
	static char Buffer[256];
	switch(GetType())
	{
	case AT_INT:
		sprintf_s(Buffer, 256, "%d",*(int*)Data);
		Return = new char[strlen(Buffer)+1];
		strcpy_s(Return, strlen(Buffer)+1, Buffer);
		break;
	case AT_FLOAT:
		sprintf_s(Buffer, 256, "%f",*(float*)Data);
		Return = new char[strlen(Buffer)+1];
		strcpy_s(Return, strlen(Buffer)+1, Buffer);
		break;
	case AT_STRING:
		Return = (char *)Data;
		break;
	case AT_UNKNOWN:
	default:
		Return = NULL;
	};
	return Return;
}

Value Value::GetTypeString() const
{
	Value Return("Error","Error");

	switch(valueType)
	{
	case AT_INT:
		Return = "Integer";
		break;
	case AT_FLOAT:
		Return = "Float";
		break;
	case AT_STRING:
		Return = "String";
		break;
	case AT_UNKNOWN:
		Return = "Unknown";
		break;
	}

	return Return;
}

Value Value::ToSendableString() const
{
	Value Return("","Name:");
	Return += this->name;
	Return += " Type:";
	Return += this->GetType();
	Return += " Value:";
	switch(GetType())
	{
	case AT_INT:
		Return += *(int*)Data;
		break;
	case AT_FLOAT:
		Return += *(float*)Data;
		break;
	case AT_STRING:
		Return += *this;
		break;
	}

	return Return;
}

void Value::SetFromSendable(char *Incoming)
{
	char *Buffer = new char[strlen(Incoming)+1];
	strcpy_s(Buffer, strlen(Incoming)+1, Incoming);
	int Int;
	float Float;
	if(strlen(Buffer) < 19)
	{
		if(Buffer)
			delete[] Buffer;
		return;
	}
	if(Buffer[0] != 'N' || Buffer[1] != 'a' || Buffer[2] != 'm' || Buffer[3] != 'e' || Buffer[4] != ':')
		{
		if(Buffer)
			delete[] Buffer;
		return;
	}
	int Pos = 5;
	while(Buffer[Pos] != ' ' && Buffer[Pos] != 0)
		Pos++;
	if(!Buffer[Pos])
	{
		if(Buffer)
			delete[] Buffer;
		return;
	}
	Buffer[Pos] = 0;
	strcpy_s(name, NAMELEN, Buffer+5);
	int Beg = Pos+1;
	Buffer[Pos] = ' ';
	Pos++;
	if(Buffer[Pos] != 'T' || Buffer[Pos+1] != 'y' || Buffer[Pos+2] != 'p' || Buffer[Pos+3] != 'e' || Buffer[Pos+4] != ':')
	{
		if(Buffer)
			delete[] Buffer;
		return;
	}
	Pos += 5;
	while(Buffer[Pos] != ' ' && Buffer[Pos] != 0)
		Pos++;
	if(!Buffer[Pos])
	{
		if(Buffer)
			delete[] Buffer;
		return;
	}
	switch(Buffer[Pos-1])
	{
	case '0':
		SetType(AT_UNKNOWN);
		break;
	case '1':
		SetType(AT_INT);
		Int = atoi(Buffer+Pos+7);
		*this = Int;
		break;
	case '2':
		SetType(AT_FLOAT);
		Float = atof(Buffer+Pos+7);
		*this = Float;
		break;
	case '3':
		SetType(AT_STRING);
		*this = Buffer+Pos+7;
		break;
	}
	if(Buffer)
		delete[] Buffer;
}

void Value::Copy(const Value &Source)
{
	if(GetType() == AT_STRING)
	{
		Data = (void *)((char*)Data-sizeof(int));
		delete[] Data;
	}
	else
		delete Data;
	if(strcmp(name, "") == 0 && strcmp(Source.name, "") != 0)
		strcpy_s(name, NAMELEN, Source.name);
	int *IntPtr, *IntPtr2;
	char *CharPtr,*CharPtr2;
	switch(Source.GetType())
	{
	case AT_INT:
		Data = new int;
		*(int *)Data = *(int*)Source.Data;
		break;
	case AT_FLOAT:
		Data = new float;
		*(float *)Data = *(float*)Source.Data;
		break;
	case AT_STRING:
		CharPtr = (char *)Source.Data;
		IntPtr = (int *)(CharPtr-sizeof(int));
		Data = new char[*IntPtr+sizeof(int)+1];
		IntPtr2 = (int *)Data;
		CharPtr2 = (char *)IntPtr2;
		CharPtr2 += sizeof(int);
		*IntPtr2 = *IntPtr;
		if(*IntPtr2)
			strcpy_s(CharPtr2, *IntPtr2+1, CharPtr);
		else
			CharPtr2[0] = 0;
		Data = (void*)CharPtr2;
		break;
	case AT_UNKNOWN:
	default:
		Data = new int;
		*(int *)Data = *(int *)Source.Data;
		break;
	};
	valueType = Source.GetType();
}

std::ostream & operator << (std::ostream &cout, Value &Source)
{
	switch(Source.GetType())
	{
	case AT_INT:
		cout << Source.ToInt();
		break;
	case AT_FLOAT:
		cout << Source.ToFloat();
		break;
	case AT_STRING:
		cout << Source.ToCharPtr();
		break;
	}

	return cout;
}

std::ofstream & operator << (std::ofstream &cout, Value &Source)
{
	switch(Source.GetType())
	{
	case AT_INT:
		cout << Source.ToInt();
		break;
	case AT_FLOAT:
		cout << Source.ToFloat();
		break;
	case AT_STRING:
		cout << Source.ToCharPtr();
		break;
	}

	return cout;
}